// Copyright 2007-2009 Stuart Caborn, Alex Scordellis
//  
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License. You may obtain a copy of
//  the License at
//  
//  http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//  License for the specific language governing permissions and limitations under
//  the License.
// 


using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NSynthesis.Reflection;

namespace NSynthesis
{
    public class MockedMethodCall
    {
        private readonly MethodInfo invokedMockCall;
        private string description;

        public MockedMethodCall(MethodInfo invokedMockCall)
        {
            this.invokedMockCall = invokedMockCall;
        }

        public bool Equals(MockedMethodCall obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return Equals(obj.Description, Description);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (MockedMethodCall)) return false;
            return Equals((MockedMethodCall) obj);
        }

        public static bool operator ==(MockedMethodCall left, MockedMethodCall right) { return Equals(left, right); }
        public static bool operator !=(MockedMethodCall left, MockedMethodCall right) { return !Equals(left, right); }

        public override int GetHashCode()
        {
            unchecked
            {
                return (Description.GetHashCode()*397) ^ Description.GetHashCode();
            }
        }

        public IEnumerable<RequiredProductionMethod> RealImplementations
        {
            get
            {
                return from registeredType in Synthesis.RegisteredTypes
                       where registeredType.CouldImplement(invokedMockCall)
                       from methodInfo in registeredType.GetMethods()
                       where methodInfo.CouldImplement(invokedMockCall)
                       select new RequiredProductionMethod(methodInfo);
            }
        }

        public string Description
        {
            get{ return description = description ?? invokedMockCall.Description(); }
        }

        public bool IsNotResolved
        {
            get{ return NoImplementationsWereDetected || MethodHasAtLeastOneMissingMethodCall; }
        }

        private bool MethodHasAtLeastOneMissingMethodCall
        {
            get { return RealImplementations.Where(productionMethod => productionMethod.HasBeenCalled == false).Any(); }
        }

        private bool NoImplementationsWereDetected
        {
            get{ return !RealImplementations.Any(); }
        }

        public int Hits { get; set; }
    }

    /// <summary>
    /// Contains a method from a registered type which has been connected to a mock call.
    /// Can detect if there are any production methods for this object - merge with Mocked method calls?
    /// I'm not sure if this class adds any real value
    /// </summary>
    public class RequiredProductionMethod
    {
        private readonly MethodInfo methodInfo;

        public RequiredProductionMethod(MethodInfo methodInfo)
        {
            this.methodInfo = methodInfo;

            RealMethodCalls = new List<RealMethodCall>(
                from realMethodCall in Synthesis.Recorder.RealMethodCalls
                where realMethodCall.Satisfies(methodInfo)
                select realMethodCall);
        }

        public List<RealMethodCall> RealMethodCalls { get; private set; }

        public bool HasBeenCalled
        {
            get { return RealMethodCalls.Count > 0; }
        }

        public string Description
        {
            get
            {
                return methodInfo.Description();
            }
        }


        public override string ToString()
        {
            return "RequiredProductionMethod for " + methodInfo.Description();
        }
    }
}